package it.gma.torero.operand;

import it.gma.torero.operator.EqualsOperator;
import it.gma.torero.operator.GreaterEqualsOperator;
import it.gma.torero.operator.GreaterOperator;
import it.gma.torero.operator.LessEqualsOperator;
import it.gma.torero.operator.LessOperator;
import it.gma.torero.util.Assert;

import java.lang.reflect.Constructor;
import java.util.Comparator;

@SuppressWarnings("unchecked")
public abstract class AbstractComparationOperand implements ComparationOperand {

	private static final ComparationOperandComparator COMPARATION_OPERAND_COMPARATOR = new ComparationOperandComparator();
	protected Object value;

	public AbstractComparationOperand(Object value) {
		super();
		this.value = value;
	}

	public Object getValue() {

		return value;
	}

	@Override
	public boolean equals(Object obj) {
		return EqualComparator.compare(this, (ComparationOperand) obj);
	}

	public int compareTo(ComparationOperand o) {
		return COMPARATION_OPERAND_COMPARATOR.compare(this, o);
	}

	@Override
	public String toString() {
		return getValue() != null ? getValue().toString() : "'null'";
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((value == null) ? 0 : value.hashCode());
		return result;
	}

	public LessOperator less(ComparationOperand operand) {
		return new LessOperator(this, operand);
	}

	public LessEqualsOperator lessOrEqual(ComparationOperand operand) {
		return new LessEqualsOperator(this, operand);
	}

	public GreaterEqualsOperator greaterOrEqual(ComparationOperand operand) {
		return new GreaterEqualsOperator(this, operand);
	}

	public EqualsOperator equal(ComparationOperand operand) {
		return new EqualsOperator(this, operand);
	}

	public GreaterOperator greater(ComparationOperand operand) {
		return new GreaterOperator(this, operand);
	}

	public abstract void setContext(VariableBinder context);

	private static abstract class EqualComparator {

		public static boolean compare(ComparationOperand this_, ComparationOperand other) {
			if (isBothNull(this_, other)) {
				return true;
			}
			if (isOnlyOneNull(this_, other)) {
				return false;
			}

			if (!this_.isConstant() && !other.isConstant()) {
				return compareWithEquals(this_, other);
			}

			if (this_.isConstant() && other.isConstant()) {
				return compareWithEquals(this_, other);
			}

			return checkEqualsOneConstantOtherNot(this_, other);

		}

		private static boolean checkEqualsOneConstantOtherNot(ComparationOperand this_, ComparationOperand other) {
			if (this_.isConstant() && !other.isConstant()) {
				return compareConstantWithValue(other.getValue(), this_.getValue());
			} else {
				return compareConstantWithValue(this_.getValue(), other.getValue());
			}

		}

		private static boolean compareConstantWithValue(Object obj, Object constant) {
			try {

				Class<?> class_ = obj.getClass();
				Constructor<?> contructor = class_.getConstructor(String.class);
				Object tocompare = contructor.newInstance(constant);

				return obj.equals(tocompare);

			} catch (Exception e) {
				return false;
			}

		}

		private static boolean compareWithEquals(ComparationOperand this_, ComparationOperand other) {
			if (!this_.getValue().equals(other.getValue()))
				return false;
			return true;
		}

	}

	private static class ComparationOperandComparator implements Comparator<ComparationOperand> {

		public int compare(ComparationOperand this_, ComparationOperand o) {
			Assert.notNull(o);

			if (isBothNull(this_, o)) {
				return 0;
			}
			if (isOnlyOneNull(this_, o)) {
				return this_.getValue() == null ? -1 : +1;
			}

			if (!this_.isConstant() && !o.isConstant()) {
				return compareWithcomparable(this_, o);
			}

			if (this_.isConstant() && o.isConstant()) {
				return compareWithcomparable(this_, o);
			}

			return this_.isConstant() ? compareConstantWithValue(o.getValue(), this_.getValue()) : compareConstantWithValue(this_.getValue(), o.getValue());
		}

		private int compareConstantWithValue(Object obj, Object constant) {
			try {

				Class<?> class_ = obj.getClass();
				Constructor<?> contructor = class_.getConstructor(String.class);
				Object tocompare = contructor.newInstance(constant);

				return compareValueWithcomparable(obj, tocompare);

			} catch (Exception e) {
				return 0;
			}
		}

		private int compareWithcomparable(ComparationOperand this_, ComparationOperand o) {
			return compareValueWithcomparable(this_.getValue(), o.getValue());

		}

		private int compareValueWithcomparable(Object this_, Object o) {
			try {
				Comparable<Object> value1 = (Comparable<Object>) this_;
				Comparable<Object> value2 = (Comparable<Object>) o;
				return value1.compareTo(value2);
			} catch (ClassCastException e) {
				return 0;
			}
		}

	}

	private static boolean isBothNull(ComparationOperand this_, ComparationOperand other) {
		return (this_.getValue() == null && other.getValue() == null);
	}

	private static boolean isOnlyOneNull(ComparationOperand this_, ComparationOperand other) {
		return (this_.getValue() == null && other.getValue() != null) || (this_.getValue() != null && other.getValue() == null);
	}

}
